/*
MIT License 

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов, 

https://bmstu.codes/lsx/simodo/loom
*/

#ifndef simodo_parser_fuze_FuzeSubsetRdp
#define simodo_parser_fuze_FuzeSubsetRdp

/*! \file FuzeRdp.h
    \brief Класс разбора синтаксических правил на подмножестве языка SIMODO fuze методом рекурсивного спуска
*/

#include "simodo/parser/SyntaxDataCollector_interface.h"
#include "simodo/parser/fuze/FuzeOperationCode.h"
#include "simodo/ast/generator/FormationFlow_interface.h"
#include "simodo/inout/token/Parser_interface.h"
#include "simodo/inout/token/Tokenizer.h"
#include "simodo/inout/token/RdpBaseSugar.h"
#include "simodo/inout/token/InputStreamSupplier_interface.h"
#include "simodo/inout/reporter/Reporter_abstract.h"

#include <string>
#include <set>
#include <map>


namespace simodo::parser
{
    /*!
     * \brief Класс начального разбора синтаксических правил языка SIMODO fuze методом рекурсивного спуска (recursive descent parser, RDP)
     *
     * Класс выполняет разбор текста с упрощённым описанием грамматики и формирует набор правил грамматики для
     * последующего построения таблицы разбора.
     *
     * Порядок работы с данным классом следующий. В конструкторе класса задаётся путь к файлам грамматики
     * и наименование грамматики (которое должно совпадать с наименованием файла). Затем нужно вызвать один
     * из методов `parse`, которые производят разбор заданной грамматики и наполняют перечень правил.
     *
     * Синтаксическая диаграмма представлена ниже.
     *
     * \dot
     * digraph sql {
     *  rankdir = LR;
     *  ranksep=.75; size = "9,9";
     *  node       [shape=Mrecord fontsize=18];
     *  start      [label="start"       style=filled fillcolor=black shape=point];
     *  import     [label="'import'"    style=filled fillcolor=yellow];
     *  imp_ia     [label="ID \| ANNOTATION" style=filled fillcolor=yellow];
     *  main       [label="'main'"      style=filled fillcolor=yellow];
     *  m_id       [label="ID"          style=filled fillcolor=yellow];
     *  r_prod     [label="ID"          style=filled fillcolor=cyan];
     *  r_assign   [label="'='"         style=filled fillcolor=cyan];
     *  r_patt_ia  [label="ID \| ANNOTATION" style=filled fillcolor=cyan];
     *  r_next     [label="'=' \| '\|'" style=filled fillcolor=cyan];
     *  r_patt_dir [label="'\<' \| '\>'" style=filled fillcolor=cyan];
     *  sc         [label="'\;'"        style=filled fillcolor=white];
     *  block_start [label="'\{'"       style=filled fillcolor=green];
     *  block      [label="SBL\*"   style=filled fillcolor=green];
     *  block_end  [label="'\}'"        style=filled fillcolor=green];
     *  block_start2 [label="'\{'"       style=filled fillcolor=green];
     *  block2      [label="SBL\*"   style=filled fillcolor=green];
     *  block_end2  [label="'\}'"        style=filled fillcolor=green];
     *  eof        [label="EOF"         style=filled fillcolor=white];
     *  end        [label="finish"      style=filled fillcolor=black shape=point];
     *  start   -> {block_start, import, main, r_prod};
     *  block_start -> {block};
     *  block   -> {block_end};
     *  block_end -> {sc};
     *  import  -> imp_ia -> sc;
     *  main    -> m_id -> sc;
     *  r_prod  -> r_assign -> r_patt_ia -> {block_start2, r_patt_ia, r_next, r_patt_dir, sc};
     *  r_patt_dir -> {block_start2, r_next, sc};
     *  r_next  -> r_patt_ia;
     *  block_start2 -> {block2};
     *  block2   -> {block_end2};
     *  block_end2 -> {r_next, sc};
     *  sc      -> {start, eof};
     *  eof     -> end;
     * }
     * \enddot
     *
     * В таблице даётся краткое описание элементов диаграммы:
     *
     * | Обозначение        | Описание
     * | :----------------- | :--------------------------
     * | ID                 | Лексема входного потока типа LexemeType::Id, обозначающая идентификатор
     * | ANNOTATION         | Лексема входного потока типа LexemeType::Annotation, обозначающая строковую константу
     * | EOF                | Лексема входного потока типа LexemeType::Empty, символизирующая конец файла
     * | SBL*               | Блок кода на SBL с упрощённым синтаксисом (вызов процедуры), который разбирается в отдельном классе FuzeSblRdp
     * | Остальные элементы | Элементы в одинарных кавычках являются разделителями, которые могут комбинироваться со знаком \|, означающим, что должен быть записан один из элементов
     *
     * Разбор заданной грамматики выполняется методом рекурсивного спуска и разделён на фрагменты, которые реализуют
     * различные методы.
     *
     * Файлы, которые обрабатываются данным классом имеют расширение `.fuze`. Они находятся в каталоге
     * `data/grammar` проекта.
     */
    class FuzeRdp : public inout::Parser_interface, public inout::RdpBaseSugar
    {
        std::string                     _path;              ///< Путь к файлу грамматики
        ast::FormationFlow_interface &  _flow; ///< Интерфейс для построения АСД

        inout::InputStreamSupplier_interface & _input_stream_supplier;
        SyntaxDataCollector_interface & _syntax_data_collector; ///< Интерфейс сборщика информации о синтаксисе

        inout::uri_index_t              _current_file_index;
        bool                            _is_ready_for_parse;

    public:
        FuzeRdp() = delete; ///< Пустой конструктор не поддерживается!

        /*!
         * \brief Конструктор класса начального разбора синтаксических правил языка
         * \param m             Интерфейсная ссылка на объект обеспечения вывода информации в вызываемую программу
         * \param path          Путь к грамматике
         * \param flow          Вспомогательный класс для построения АСД
         */
        FuzeRdp(inout::Reporter_abstract & m,
                const std::string & path,
                ast::FormationFlow_interface & flow);

        /*!
         * \brief Конструктор класса начального разбора синтаксических правил языка
         * \param m             Интерфейсная ссылка на объект обеспечения вывода информации в вызываемую программу
         * \param path          Путь к грамматике
         * \param flow          Вспомогательный класс для построения АСД
         * \param input_stream_supplier Поставщик входных потоков
         * \param syntax_data_collector Интерфейс сборщика информации о синтаксисе
         */
        FuzeRdp(inout::Reporter_abstract & m,
                const std::string & path,
                ast::FormationFlow_interface & flow,
                inout::InputStreamSupplier_interface & input_stream_supplier,
                SyntaxDataCollector_interface & syntax_data_collector);

        /*!
         * \brief Метод разбора файла описания грамматики
         *
         * Метод выполняет разбор и наполнение ast. В качестве расширения файла используется
         * `.fuze`.
         *
         * Метод выполняет создание токенайзера на описанном выше файле и вызывает одноимённый метод `parse` с
         * созданным токенайзером.
         *
         * \return true, если разбор завершился без ошибок, иначе - false
         */
        virtual bool parse() override;

        /*!
         * \brief Метод разбора из заданного потока
         * \param stream        Входной поток
         * \return true, если разбор завершился без ошибок, иначе - false
         */
        virtual bool parse(inout::InputStream_interface & stream) override;

        /*!
         * \brief Геттер для поставщика входных потоков
         */
        inout::InputStreamSupplier_interface & input_stream_supplier() { return _input_stream_supplier; }

    protected:
        /*!
         * \brief Метод анализа операторов `import` и `main`
         * \param tzer Токенайзер, предоставляющий входной поток токенов языка (лексический анализатор)
         * \param t    Текущий токен
         * \return true, если разбор завершился без ошибок, иначе - false
         */
        bool    parseKeyword(inout::Tokenizer & tzer, inout::Token & t);

        /*!
         * \brief Метод анализа и формирования продукций языка по описанию его грамматики
         * \param id   Наименование продукции
         * \param tzer Токенайзер, предоставляющий входной поток токенов языка (лексический анализатор)
         * \param t    Текущий токен
         * \return true, если разбор завершился без ошибок, иначе - false
         */
        bool    parseProduction(const inout::Token & id, inout::Tokenizer & tzer, inout::Token & t,
                                FuzeOperationCode operation_code=FuzeOperationCode::Production);

        /*!
         * \brief Метод анализа и формирования шаблона, обозначения действия и направления ассоциативности для конкретного правила грамматики
         * \param tzer Токенайзер, предоставляющий входной поток токенов языка (лексический анализатор)
         * \param t    Текущий токен
         * \return true, если разбор завершился без ошибок, иначе - false
         */
        bool    parsePattern(inout::Tokenizer & tzer, inout::Token & t);

        /*!
         * \brief Геттер на вспомогательный класс для построения АСД
         * \return Ссылка на вспомогательный класс для построения АСД
         */
        ast::FormationFlow_interface & ast() { return _flow; }

        /*!
         * \brief Метод-вроппер для сбора информации о токенах (в том числе комментариев)
         * \param tzer Токенайзер, предоставляющий входной поток токенов языка (лексический анализатор)
         * \return Первый токен не комментарий из входного потока
         */
        inout::Token   getToken(inout::Tokenizer & tzer) const ;

        void initiateFileForParse();
    };
}

#endif // simodo_parser_fuze_FuzeSubsetRdp
